#pragma once

#include <string>
#include "bitset.hpp"

namespace simulation {
	struct BKDRHash {
		inline size_t operator()(const std::string& key)
		{
			size_t hash = 0;
			for (auto ch : key)
			{
				hash *= 131;
				hash += ch;
			}
			return hash;
		}
	};

	struct APHash {
		inline size_t operator()(const std::string& key)
		{
			unsigned int hash = 0;
			int i = 0;

			for (auto ch : key)
			{
				if ((i & 1) == 0)
				{
					hash ^= ((hash << 7) ^ (ch) ^ (hash >> 3));
				}
				else
				{
					hash ^= (~((hash << 11) ^ (ch) ^ (hash >> 5)));
				}

				++i;
			}

			return hash;
		}
	};

	struct DJBHash {
		inline size_t operator()(const std::string& key)
		{
			unsigned int hash = 5381;

			for (auto ch : key)
			{
				hash += (hash << 5) + ch;
			}

			return hash;
		}
	};

	struct JSHash {
		inline size_t operator()(const std::string& s)
		{
			size_t hash = 1315423911;
			for (auto ch : s)
			{
				hash ^= ((hash << 5) + ch + (hash >> 2));
			}
			return hash;
		}
	};

	// 假设N是最多存储的数据个数
	// 平均存储一个值，开辟X个位
	template<
		size_t N,
		size_t X = 6,
		class K = std::string,
		class Hash1 = BKDRHash,
		class Hash2 = APHash,
		class Hash3 = DJBHash,
		class Hash4 = JSHash>
	class bloomfilter {
	private:
		BitSet<N * X> _bs;
		const size_t _size = N * X;

	public:
		void set(const K& str)
		{
			size_t index1 = Hash1()(str) % _size;
			size_t index2 = Hash2()(str) % _size;
			size_t index3 = Hash3()(str) % _size;
			size_t index4 = Hash4()(str) % _size;

			_bs.set(index1);
			_bs.set(index2);
			_bs.set(index3);
			_bs.set(index4);
		}

		bool test(const K& str) const
		{
			//size_t index1 = Hash1()(str) % _size;
			//size_t index2 = Hash2()(str) % _size;
			//size_t index3 = Hash3()(str) % _size;
			//size_t index4 = Hash4()(str) % _size;

			//return _bs.test(index1)
			//	&& _bs.test(index2)
			//	&& _bs.test(index3)
			//	&& _bs.test(index4);

			size_t index1 = Hash1()(str) % _size;
			if (!_bs.test(index1)) return false;

			size_t index2 = Hash2()(str) % _size;
			if (!_bs.test(index2)) return false;

			size_t index3 = Hash3()(str) % _size;
			if (!_bs.test(index3)) return false;

			size_t index4 = Hash4()(str) % _size;
			if (!_bs.test(index4)) return false;

			return true;
		}
	};
}